#include "hemisphere.h"
#include <cmath>
#include <GL/gl.h>
#include "rectangle.h"
#include "triangle.h"
#include <cstring>

#define TO_RAD(x) ((x)*M_PI / 180.0)

Hemisphere::Hemisphere()
{
    for(double phi = 0.0 ; phi >= -80.0 ; phi -= 10.0) {
        double phir = TO_RAD(phi);
        double phir10 = TO_RAD(phi - 10.0);
        Edge *prev_edge = 0;

        for( double theta = -180.0 ; theta <= 180.0 ; theta += 20.0 ) {
            double thetar = TO_RAD(theta);
            Point *p1 = new Point(sin(thetar)*cos(phir), cos(thetar)*cos(phir), sin(phir));
            Point *p2 = new Point(sin(thetar)*cos(phir10), cos(thetar)*cos(phir10), sin(phir10));

            Edge *new_prev = new Edge(p1, p2);
            Edge *e2 = new Edge(p2, p1);
            if( prev_edge ) {
                Edge *e1 = new Edge(prev_edge->second, p2);
                Edge *e3 = new Edge(p1, prev_edge->first);

                Face f;
                f.push_back(prev_edge);
                f.push_back(e1);
                f.push_back(e2);
                f.push_back(e3);
                faces_.push_back(f);
            } else {
                prev_edge = new Edge();
            }

            prev_edge = new_prev;
            vertices_.push_back(p1);
            vertices_.push_back(p2);
        }
    }

//    Point *apex = new Point(0.0, 0.0, -1.0);
//    vertices_.push_back(apex);
//    cap.push_back(apex);
//    double capangle = TO_RAD(-70.0);
//    for(double angle = -180.0 ; angle <= 160.0 ; angle+=20.0) {
//        Point *p = new Point(sin(angle)*cos(capangle), cos(angle)*cos(capangle), sin(capangle));
//        vertices_.push_back(p);
//        cap.push_back(p);
//    }

    material_.set_ambient(CgColor(0.4, 0.4, 0.4));
    material_.set_diffuse(CgColor(0.4, 0.4, 0.4));
    material_.set_specular(CgColor(0.0, 0.0, 0.0));
}

// Tests for intersection with each rectangular face of the hemisphere
bool Hemisphere::intersect(const Ray &r, double &t, Vector &normal) const
{
    std::vector<Face>::const_iterator it;
    double temp_t;
    Vector temp_normal;

    t = -1.0;
    for(it = faces_.begin() ; it != faces_.end() ; it++) {
        const Face &f = *it;
        Rectangle rec(*(f[0]->first), *(f[1]->first), *(f[2]->first), *(f[3]->first));

        if( rec.intersect(r, temp_t, temp_normal) &&
            (t < 0.0 || temp_t <= t) ) {
            t = temp_t;
            normal = temp_normal;
        }
    }

//    for(size_t i = 1 ; i < cap.size() ; i++) {
//        size_t index = (i+1) % cap.size();
//        if(index == 0)
//            index = 1;
//
//        Point *p1 = cap[i];
//        Point *p2 = cap[index];
//        Triangle triangle(vertices_[0], p1, p2);
//        if( triangle.intersect(r, temp_t, temp_normal) &&
//            (t < 0.0 || temp_t <= t) ) {
//            t = temp_t;
//            normal = temp_normal;
//        }
//    }

    return t > 0.0;
}

void Hemisphere::openGlRender() const
{
    std::vector<Face>::const_iterator it;
    setup_gl_materials();

    glBegin(GL_QUADS);
    for(it = faces_.begin() ; it != faces_.end() ; it++) {
        const Face &f = *it;
        Vector normal = vecprod(*(f[1]->first) - *(f[0]->first), *(f[2]->first) - *(f[0]->first));
        normal = normal.normalize();
        glNormal3d(normal(0), normal(1), normal(2));
        glVertex3dv(f[0]->first->toCArray());
        glVertex3dv(f[1]->first->toCArray());
        glVertex3dv(f[2]->first->toCArray());
        glVertex3dv(f[3]->first->toCArray());
    }
    glEnd();
}

ICgObject* Hemisphere::clone() const
{
    Hemisphere *h = new Hemisphere();
    h->multiplyCurrentTransformation(this->transformation_);
    h->material_ = this->material_;
    return h;
}
